package com.na.game.engine.opengl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.microedition.khronos.opengles.GL10;

import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Typeface;

import com.na.game.engine.util.GameRuntimeException;

public class GLTextureManager {

	public static final int DEFAULT_TEXTURE_WIDTH = 2048;
	public static final int DEFAULT_TEXTURE_HEIGHT = 2048;
	
	// 当前可用的材质
	protected static List<GLTexture> textures = new ArrayList<GLTexture>();
	protected static Map<GLImage, GLTexture> imageToTexture = new HashMap<GLImage, GLTexture>();
	protected static Map<GLTexture, List<GLImage>> textureToImage = new HashMap<GLTexture, List<GLImage>>();
	protected static Map<Font, GLTextImage> fontImages = new HashMap<Font, GLTextImage>();
	// 被回收的材质
	protected static List<GLTexture> recycledTextures = new ArrayList<GLTexture>();
	// 材质使用情况(使用为0的材质才能被回收)
	protected static Map<GLTexture, Integer> textureRegisterCount = new HashMap<GLTexture, Integer>();
	
	protected static GLTexture registerImage(GLImage image) {
		return registerImage(image, DEFAULT_TEXTURE_WIDTH, DEFAULT_TEXTURE_HEIGHT);
	}
	
	protected static GLTexture registerImage(GLImage image, int width, int height) {
		GLTexture texture = imageToTexture.get(image);
		if (texture != null) {
			registerTexture(texture);
			return texture;
		}
		
		int size = textures.size();
		for (int i = 0; i < size; i++) {
			texture = textures.get(i);
			if (texture.register(image)) {
				registerTexture(texture);
				imageToTexture.put(image, texture);
				List<GLImage> all = textureToImage.get(texture);
				if (all == null) {
					all = new ArrayList<GLImage>();
					textureToImage.put(texture, all);
				}
				all.add(image);
				return texture;
			}
		}
		
		texture = createTexture(width, height);
		if (texture.register(image)) {
			registerTexture(texture);
			textures.add(texture);
			imageToTexture.put(image, texture);
			List<GLImage> all = textureToImage.get(texture);
			if (all == null) {
				all = new ArrayList<GLImage>();
				textureToImage.put(texture, all);
			}
			all.add(image);
			return texture;
		}
		
		throw new GameRuntimeException("image too large");
	}
	
	protected static GLTexture registerFont(String text, Paint paint, FontMetricsInt fmi) {
		GLTextImage image = getFontImage(text, paint);
		if (image == null) {
			image = new GLTextImage();
			image.drawText(text, paint, fmi);
			fontImages.put(fontKey.clone(), image);
		}
		
		return registerImage(image);
	}
	
	protected static GLTextImage getFontImage(String text, Paint paint) {
		fontKey.update(text, (int) paint.getTextSize(), paint.getTypeface());
		GLTextImage image = fontImages.get(fontKey);
		return image;
	}
	
	private static GLTexture createTexture(int width, int height) {
		for (int i = recycledTextures.size() - 1; i >= 0; i--) {
			GLTexture texture = recycledTextures.get(i);
			if (texture.width == width && texture.height == height) {
				recycledTextures.remove(i);
				return texture;
			}
		}
		
		return new GLTexture(width, height);
	}
	
	private static void registerTexture(GLTexture texture) {
		Integer count = textureRegisterCount.get(texture);
		if (count == null) {
			count = Integer.valueOf(0);
		}
		textureRegisterCount.put(texture, count.intValue() + 1);
	}
	
	private static int unRegisterTexture(GLTexture texture) {
		Integer count = textureRegisterCount.get(texture);
		if (count == null || count.intValue() <= 0) {
			return 0;
		}
		int newValue = count.intValue() - 1;
		textureRegisterCount.put(texture, Integer.valueOf(newValue));
		return newValue;
	}
	
	public static void clear() {
		for (int i = textures.size() - 1; i >= 0; i--) {
			GLTexture texture = textures.get(i);
			if (unRegisterTexture(texture) == 0) {
				List<GLImage> all = textureToImage.remove(texture);
				for (int j = all != null ? all.size() - 1 : -1; j >= 0; j--) {
					GLImage image = all.get(j);
					imageToTexture.remove(image);
				}
				
				texture.clear();
				textures.remove(i);
				recycledTextures.add(texture);
			}
		}
	}
	
	public static void destroy(GL10 gl) {
		imageToTexture.clear();
		textureToImage.clear();
		fontImages.clear();
		textureRegisterCount.clear();
		for (int i = textures.size() - 1; i >= 0; i--) {
			textures.get(i).destroy(gl);
		}
		for (int i = recycledTextures.size() - 1; i >= 0; i--) {
			recycledTextures.get(i).destroy(gl);
		}
		textures.clear();
		recycledTextures.clear();
	}
	
	private static Font fontKey = new Font();
	static class Font {
		String text;
		int size;
		Typeface typeface;
		Font() {
		}
		Font(String text, int size, Typeface typeface) {
			update(text, size, typeface);
		}
		void update(String text, int size, Typeface typeface) {
			this.text = text;
			this.size = size;
			this.typeface = typeface;
		}
		@Override
		public boolean equals(Object o) {
			if (o == this) {
				return true;
			}
			if (o instanceof Font) {
				Font other = (Font) o;
				return text != null ? text.equals(other.text) && size == other.size && typeface == other.typeface : false;
			}
			return false;
		}
		@Override
		public int hashCode() {
			return text != null ? text.hashCode() : 0;
		}
		@Override
		public Font clone() {
			return new Font(text, size, typeface);
		}
	}
}
